unit NUtils;

interface
uses Math, Forms, Types, SysUtils, Graphics, Windows, Classes, NCore;

//-------------------------------------
// Settings
//-------------------------------------
const
 Epsilon   = 1.0E-12;
 
type
TFileVersionInfo = record
 FCompanyName      : string;
 FFileDescription  : string;
 FFileVersion      : string;
 FInternalName     : string;
 FLegalCopyright   : string;
 FLegalTradeMarks  : string;
 FOriginalFilename : string;
 FProductName      : string;
 FProductVersion   : string;
 FComments         : string;
 FIsRegistered     : string;
end;

TScannerMode = record
 Name: String;
 Code: Integer;
end;

TArrayOfTScannerMode = array of TScannerMode;

function  ExtractionStatusToStr(err: TNleExtractionStatus): String;

function  MatchingThresholdToString(value: Integer): string;
function  MatchingThresholdFromString(value: string): Integer;

function  MaximalRotationFromDegrees(value: Integer): Byte;
function  MaximalRotationToDegrees(value: Byte): Integer;

function  QualityToPercent(value: byte): Integer;
function  QualityFromPercent(value: integer) : Byte;

procedure FillScannerModes(var scannerModes: TArrayOfTScannerMode);

function  GetVersionInfo(const FileName: String): TFileVersionInfo;
procedure QuickSort(var A: TArrayOfMatchResult; L, R: Integer; var tmp: TMatchResult);
function  LocalAppDataPath : string;

//-------------------------------------
// Implementation of functions
//-------------------------------------
implementation
 uses NBiometrics, SHFolder;

function LocalAppDataPath : string;
const
   SHGFP_TYPE_CURRENT = 0;
var
   path: array [0..MAX_PATH] of char;
begin
    SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0, SHGFP_TYPE_CURRENT, @path[0]) ;
    Result := path;
end;

function MatchingThresholdToString(value: Integer): string;
 var p : double;
     tmp: integer;
     tmp2: Double;
     str: string;
begin
 p := - value / 12.0;
 tmp := Max(0, Ceil(-p) - 2);
 tmp2 := Power(10, p)*100;
 str := '%.'+IntToStr(tmp)+'f';
 str := Format(str,[tmp2]);
 Result := str;
end;

function MatchingThresholdFromString(value: string): Integer;
 var p: double;
begin
 p := Log10(Max(Epsilon,Min(1,StrToFloat(Trim(value))/100)));
 Result := Max(0, Round(-12 * p));
end;

function ExtractionStatusToStr(err: TNleExtractionStatus): String;
begin
  case err of
   nleesNone: Result := 'None';
   nleesTemplateCreated: Result := 'Template create';
   nleesFaceNotDetected: Result := 'Face not detected';
   nleesEyesNotDetected: Result := 'Eyes not detected';
   nleesFaceTooCloseToImageBorder: Result := 'Face too close to image border';
   nleesQualityCheckGrayscaleDensityFailed: Result := 'Quality check - grayscale density failed';
   nleesQualityCheckExposureFailed: Result := 'Quality check - exposure failed';
   nleesQualityCheckSharpnessFailed: Result := 'Quality check - sharpness failed';
   nleesLivenessCheckFailed: Result := 'Liveness check failed';
   nleesGeneralizationFailed: Result := 'Generalization failed';
  else
     Result := 'Unknown';
 end;
end;

function QualityToPercent(value: byte): Integer;
begin
 Result := (2 * value * 100 + 255) div (2 * 255);
end;

function QualityFromPercent(value: integer) : Byte;
begin
 Result := Byte((2 * value * 255 + 100) div (2 * 100));
end;

function MaximalRotationToDegrees(value: Byte): Integer;
begin
 Result := (2*value*360 + 256) div (2*256);
end;

function MaximalRotationFromDegrees(value: Integer): Byte;
begin
  Result := Byte((2 * value * 256 + 360) div (2 * 360));
end;

procedure FillScannerModes(var scannerModes: TArrayOfTScannerMode);
 var ExtractorStruct: TScannerMode;
begin
 SetLength(scannerModes, 22);

 ExtractorStruct.Name := 'General';
 ExtractorStruct.Code := N_MODE_GENERAL;
 scannerModes[0] := ExtractorStruct;

 ExtractorStruct.Name := 'Digital Persona UareU';
 ExtractorStruct.Code := N_MODE_DIGITALPERSONA_UAREU;
 scannerModes[1] := ExtractorStruct;

 ExtractorStruct.Name := 'Biometrika FX2000';
 ExtractorStruct.Code := N_MODE_BIOMETRIKA_FX2000;
 scannerModes[2] := ExtractorStruct;

 ExtractorStruct.Name := 'Biometrika FX3000';
 ExtractorStruct.Code := N_MODE_BIOMETRIKA_FX3000;
 scannerModes[3] := ExtractorStruct;

 ExtractorStruct.Name := 'Keytronic Secure Desktop';
 ExtractorStruct.Code := N_MODE_KEYTRONIC_SECUREDESKTOP;
 scannerModes[4] := ExtractorStruct;

 ExtractorStruct.Name := 'Identix Touch View';
 ExtractorStruct.Code := N_MODE_IDENTIX_TOUCHVIEW;
 scannerModes[5] := ExtractorStruct;

 ExtractorStruct.Name := 'Identix DFR2090';
 ExtractorStruct.Code := N_MODE_IDENTIX_DFR2090;
 scannerModes[6] := ExtractorStruct;

 ExtractorStruct.Name := 'Precise Biometrics 100CS';
 ExtractorStruct.Code := N_MODE_PRECISEBIOMETRICS_100CS;
 scannerModes[7] := ExtractorStruct;

 ExtractorStruct.Name := 'UPEK TouchChip';
 ExtractorStruct.Code := N_MODE_UPEK_TOUCHCHIP;
 scannerModes[8] := ExtractorStruct;

 ExtractorStruct.Name := 'Identicator Technology DF90';
 ExtractorStruct.Code := N_MODE_IDENTICATORTECHNOLOGY_DF90;
 scannerModes[9] := ExtractorStruct;

 ExtractorStruct.Name := 'Atmel FingerChip';
 ExtractorStruct.Code := N_MODE_ATMEL_FINGERCHIP;
 scannerModes[10] := ExtractorStruct;

 ExtractorStruct.Name := 'BMF BLP100';
 ExtractorStruct.Code := N_MODE_BMF_BLP100;
 scannerModes[11] := ExtractorStruct;

 ExtractorStruct.Name := 'Secugen Hamster';
 ExtractorStruct.Code := N_MODE_SECUGEN_HAMSTER;
 scannerModes[12] := ExtractorStruct;

 ExtractorStruct.Name := 'Crossmatch Verifier 300';
 ExtractorStruct.Code := N_MODE_CROSSMATCH_VERIFIER300;
 scannerModes[13] := ExtractorStruct;

 ExtractorStruct.Name := 'NITGEN Fingkey Hamster';
 ExtractorStruct.Code := N_MODE_NITGEN_FINGKEY_HAMSTER;
 scannerModes[14] := ExtractorStruct;

 ExtractorStruct.Name := 'Testech Bio-i';
 ExtractorStruct.Code := N_MODE_TESTECH_BIOI;
 scannerModes[15] := ExtractorStruct;

 ExtractorStruct.Name := 'Digent Izzix 1000';
 ExtractorStruct.Code := N_MODE_DIGENT_IZZIX;
 scannerModes[16] := ExtractorStruct;

 ExtractorStruct.Name := 'Startek FM200';
 ExtractorStruct.Code := N_MODE_STARTEK_FM200;
 scannerModes[17] := ExtractorStruct;

 ExtractorStruct.Name := 'Fujitsu MBF200';
 ExtractorStruct.Code := N_MODE_FUJITSU_MBF200;
 scannerModes[18] := ExtractorStruct;

 ExtractorStruct.Name := 'Futronics''s FS80';
 ExtractorStruct.Code := N_MODE_FUTRONIC_FS80;
 scannerModes[19] := ExtractorStruct;

 ExtractorStruct.Name := 'LighTuning LTT-C500';
 ExtractorStruct.Code := N_MODE_LIGHTUNING_LTTC500;
 scannerModes[20] := ExtractorStruct;

 ExtractorStruct.Name := 'Tacoma CMOS';
 ExtractorStruct.Code := N_MODE_TACOMA_CMOS;
 scannerModes[21] := ExtractorStruct;
end;

procedure QuickSort(var A: TArrayOfMatchResult; L, R: Integer; var tmp: TMatchResult);
var
  OrigL,
  OrigR: Integer;
  Pivot: Double;
  GoodPivot,
  SortPartitions: Boolean;
begin
  if L<R then begin
    Pivot:=A[L+Random(R-L)].FSimilarity;
    OrigL:=L; //saving original bounds
    OrigR:=R;
    repeat
      L:=OrigL; //restoring original bounds if we
      R:=OrigR; //have chosen a bad pivot value
      while L<R do begin
        while (L<R) and (A[L].FSimilarity<Pivot) do Inc(L);
        while (L<R) and (A[R].FSimilarity>=Pivot) do Dec(R);
        if (L<R) then begin
          tmp:=A[L];
          A[L]:=A[R];
          A[R]:=tmp;
          Dec(R);
          Inc(L);
        end;
      end;
      if A[L].FSimilarity>=Pivot then Dec(L);                            //have we managed to choose
      GoodPivot:=L>=OrigL;                                              //a good pivot value?
      SortPartitions:=True;                                             //if so, then sort on
      if not GoodPivot then begin                                       //bad luck, the pivot is the smallest one in our range
        GoodPivot:=True;                                                //let's presume that all the values are equal to pivot
        SortPartitions:=False;                                          //then no need to sort it
        for R := OrigL to OrigR do if A[R].FSimilarity<>Pivot then begin //we have at least one different value than our pivot
          Pivot:=A[R].FSimilarity;                                       //so this will be our new pivot
          GoodPivot:=False;                                             //we have to start again sorting this range
          Break;
        end;
      end;
    until GoodPivot;
    if SortPartitions then begin
      QuickSort(A, OrigL, L, tmp);
      QuickSort(A, L+1, OrigR, tmp);
    end;
  end;
end;

function GetKeyVersionInfo(FileName, Key: String): String;
const
 vqvFmt = '\StringFileInfo\%4.4x%4.4x\%s';
var
 vlen: DWord;
 FInfoSize : longint;
 FInfo : pointer;
 FLang  : PInteger;
 //FLangNum : integer;
 vptr : pchar;
begin
 Result:= '';
 FInfoSize:= GetFileVersionInfoSize(pchar(FileName), vlen);
 if FInfoSize > 0 then
 begin
  GetMem(FInfo, FInfoSize);
  if GetFileVersionInfo(pchar(FileName), vlen, FInfoSize, FInfo) then
  begin
   VerQueryValue(FInfo, '\VarFileInfo\Translation', Pointer(FLang), vlen);
  // FLangNum:= vlen div 4;
   if VerQueryValue(FInfo, PChar(Format(vqvFmt,[LoWord(FLang^), HiWord(FLang^), Key])),
       pointer(vptr), vlen) then
     Result:= vptr;
  end;
  FreeMem(FInfo, FInfoSize);
 end;
end;

function GetVersionInfo(const FileName: String): TFileVersionInfo;
 var ResultInfo: TFileVersionInfo;
begin
 with ResultInfo do begin
  FCompanyName       := GetKeyVersionInfo(FileName, 'CompanyName');
  FFileDescription   := GetKeyVersionInfo(FileName, 'FileDescription');
  FFileVersion       := StringReplace(GetKeyVersionInfo(FileName, 'FileVersion'),',','.',[rfReplaceAll]);
  FInternalName      := GetKeyVersionInfo(FileName, 'InternalName');
  FLegalCopyright    := GetKeyVersionInfo(FileName, 'LegalCopyright');
  FLegalTradeMarks   := GetKeyVersionInfo(FileName, 'LegalTradeMarks');
  FOriginalFilename  := GetKeyVersionInfo(FileName, 'OriginalFilename');
  FProductName       := GetKeyVersionInfo(FileName, 'ProductName');
  FProductVersion    := StringReplace(GetKeyVersionInfo(FileName, 'ProductVersion'),',','.',[rfReplaceAll]);
  FComments          := GetKeyVersionInfo(FileName, 'Comments');
  FIsRegistered      := '';
 end;
 Result := ResultInfo;
end;

end.
